package in.falkeninc.umt_v0_9_8_1;

import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.support.v4.app.NotificationCompat;
import android.widget.Toast;

@SuppressWarnings({ "deprecation", "unused" })
@SuppressLint({ "Wakelock", "SimpleDateFormat" })
public class AlarmScheduler extends BroadcastReceiver  {

	final public static String ONE_TIME = "onetime";
	final public static int LONG_TIME = 0;
	
	/**	
	 * This runs when the alarm is set off.
	*/	
	@Override
	public void onReceive(Context context, Intent intent)
	{			
		PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);				// Set up PM
		PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "YOUR TAG");
		
		wl.acquire();																					// Acquire the lock

		Bundle extras = intent.getExtras();																// Get AlarmID from main thread
		int alarmid;
		if (extras != null) {
			Log.d("onReceive:", "extras found.");
			alarmid = extras.getInt("alarmid");
		} else {
			Log.d("onReceive:", "extras not found.");
			alarmid = 0;
		}

//		Intent sintent = new Intent(context, AlarmIntentService.class);
//		sintent.putExtra("alarmid", alarmid);
//    	context.startService(sintent);         

		DatabaseHandler db = new DatabaseHandler(context);    	
		AlarmInfo ai  = new AlarmInfo();

		ai = db.getAlarm(alarmid);																		// getAllAlarms() now returns a sorted list by alarm time (milliseconds) so 0 is the first:
		
    	if (ai == null) return;																			// db failed

    	int id = ai.getID();
    	long time = ai.getAlarmtime();
		
		Log.d("result:", String.valueOf(ai.getID()) + ", " + String.valueOf(ai.getName()));
         
		StringBuilder msgStr = new StringBuilder();														// You can do the processing here update the widget/remote views.
         
         if(extras != null && extras.getBoolean(ONE_TIME, Boolean.FALSE)) {
        	 msgStr.append("One time Timer : ");
         } else return;
         
         Format formatter = new SimpleDateFormat("hh:mm:ss a");											// Yeah so if you could stop changing the time API every Thursday, that would be great
         msgStr.append(formatter.format(new Date()));

         msgStr.append(String.valueOf(alarmid));
         
         Toast.makeText(context, msgStr, Toast.LENGTH_LONG).show();
         
         Intent notintent = new Intent(context, AlarmScheduler.class);
         PendingIntent conintent = PendingIntent.getActivity(context, LONG_TIME, notintent, PendingIntent.FLAG_ONE_SHOT);
         Resources res	 = context.getResources();
         NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
         NotificationManager 	nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
         builder.setContentIntent(conintent)

         .setContentTitle(ai.getName())
         	.setContentText(ai.getDescription())
	        .setSmallIcon(R.drawable.ic_launcher)
	        .setWhen(System.currentTimeMillis())
	        .setAutoCancel(true)
	        .setSound(Settings.System.DEFAULT_NOTIFICATION_URI)
	        .setTicker(ai.getDescription())																//.setTicker(res.getString(R.string.your_ticker))	        
	        .setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.alarm_icon_64));
         
         Notification n = builder.getNotification();
         nm.notify(LONG_TIME, n);

     	int enabled = 	ai.getEnabled();
     	int snooze	=	ai.getSnooze();
     	int repeat	=	ai.getRepeats();
     	
     	/** 
     	 * Now done using the alarm object and the database, now DELETE the database entry.
     	 * TODO: At some point we must reschedule depending on repeat/snooze, but BEFORE there
     	 */
         
/*     	AlarmManager am=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
     	intent  = new Intent(context, AlarmScheduler.class);
        PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
        am.set(AlarmManager.RTC_WAKEUP, calculate_alarm_schedule(context), pi);*/

        // db.deleteAlarm(ai);
		// Remove from list and alarm manager
        
		// this.CancelAlarm(context);
     	/*
     	 * Now reschedule alarm for next active week day. 
     	 * 
     	 *
     	 */
     	
     	reschedule_alarms(context);

     	wl.release();        																			//Release the lock 
	}	
	
	public int reschedule_alarms(Context context)
	{
		AlarmInfo ai = calculate_alarm_schedule(context);
	
		if (ai == null) {																				// We probably have an alarm that has gone off and may need rescheduling next day
			return -1;																					// Return -1 error code
		} else {
			set_one_time_timer(context, ai.getID());
			return ai.getID();
		}
	}

	public void set_alarm(Context context)
    {
		AlarmManager am=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
		Intent intent = new Intent(context, AlarmScheduler.class);
        intent.putExtra(ONE_TIME, Boolean.FALSE);
        PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
        //After after 30 seconds
        am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000 * 5 , pi); 
    }

    public void cancel_alarm(Context context)
    {
    	Intent intent  = new Intent(context, AlarmScheduler.class);
        PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0);
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        alarmManager.cancel(sender);
    }
    
    public void set_one_time_timer(Context context, int alarmid)
    {
    	
    	AlarmManager am=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
    	Intent intent  = new Intent(context, AlarmScheduler.class);

        intent.putExtra(ONE_TIME, Boolean.TRUE);
        intent.putExtra("alarmid", alarmid);

        PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
        DatabaseHandler db = new DatabaseHandler(context);

        am.set(AlarmManager.RTC_WAKEUP, db.getAlarm(alarmid).getAlarmtime(), pi);
        db.close();

    }

	/**
	 * Algorithm:
	 * 0. Aquire list over user created alarm entries. This is sorted by time, but the problem is that we need to mask away the date aspect of it an only look at the time, so point 1.1:
	 * 1. Iterate over list, convert to date object, mask out date, put into hashmap with alarm ID as key.
	 * 2. Sort by time, so the most recent alarm is taken care of first
	 * 3. Get first alarm object from list.
	 * 4. Check if alarm has been disabled
	 * 6. Check if alarm is on a configured weekday [1-7], if so: (and keep values so we can schedule next alarm)
	 * 7. Check if alarm is on a configured week [1-4], if so:
	 * 8. Check if alarm is on a configured month [1-12] if so:
	 * 9. Check if alarm is snoozeable, if so:
	 * A. Get snooze counter from DB, check if done, if so: skip alarm, reset snooze counter
	 * B. Get repeat counter from DB (if enabled), check if done, if so: skip alarm, check if action configured, perform it, and then reset repeat counter
	 * 5. Set off alarm
	 * 6. If snoozable an snoozed, decrease counter, schedule alarm current+snooze time
	 * 
	 * @param context - we need the context to reach the database, this comes from the calling instance in DetailsFragment
	 * @return long alarmtime - the next alarm to go off accoding to DB
	 */
    private AlarmInfo calculate_alarm_schedule(Context context) {

		DatabaseHandler db = new DatabaseHandler(context);    	
    	AlarmInfo ai  = new AlarmInfo();    	
    	ArrayList<AlarmInfo> results = db.getAllAlarms();			// getallalarms() now returns a sorted list by alarm time (milliseconds) so 0 is the first:
		
    	if (results == null) {
            Log.d ("calarm:", "DB resultset failed");
            return null;												// db failed, bail out, return error code
    	}

/*    	HashMap<Integer, Long> hmap = new HashMap<Integer, Long>();	// no guarantees as to the order of the map */
    	    	
    	results = sort_resultset(results);
    	ai = get_first_alarm_after_current_time(results);			// this doesn't capture alarms set before current time that was just snoozed or is about to repeat for the first time (or later)    	
    	results = sort_resultset_full_time(results);
    	
    	if (ai == null) return null;
    	/** 
    	 * TODO: Check for repeat snooze here ?
    	 * Because the actual time to go off on these events are not in the database
    	 * Or shall we schedule them into the DB as usual ? That would make the DB lookup part ugly (have to load 3 alarms)
    	 * So keep in one alarm and look up just that one to see.
    	 * And at this point we have the most accurate picture we 
    	 * can draw from the db. So yes.
    	 * But how to schedule an alarm from within an alarm ? :p
    	 * 
    	 */

    	//ai = results.get(0);										// this is the first alarm in the list to go off, regardless of date 	
/*    	if (ai == null) {
            Log.d ("calarm:", "AI was null");
    		return when;											// db failed, outta here
    	} */
    	
/*    	if (ai.getEnabled() == 0 ) {
            Log.d ("calarm:", "getEnabled was disbled (0)");
    		return when;											// user disabled alarm, leave gracefully
    	}
*/
    	Log.d ("calarm:", "Alarm investigated further...");
    	
    	//long alarmtime = ai.getAlarmtime();						// get the actual alarm that was set by the UI, then determine if the alarm should go off
    	
    	AFW_String astr = new AFW_String();
    	
    	String daycode = ai.getDaycode();							// check if alarm is supposed to run today
    	Calendar cal = Calendar.getInstance();
    	cal.setFirstDayOfWeek(Calendar.MONDAY);
    	
    	if (daycode.contains("1") && Calendar.DAY_OF_WEEK== Calendar.MONDAY) {
    		return ai;
    	} else if (daycode.contains("2") && Calendar.DAY_OF_WEEK== Calendar.TUESDAY) {
    		return ai;
    	} else if (daycode.contains("3") && Calendar.DAY_OF_WEEK== Calendar.WEDNESDAY) {
    		return ai;
    	} else if (daycode.contains("4") && Calendar.DAY_OF_WEEK== Calendar.THURSDAY) {
    		return ai;
    	} else if (daycode.contains("5") && Calendar.DAY_OF_WEEK== Calendar.FRIDAY) {
    		return ai;
    	} else if (daycode.contains("6") && Calendar.DAY_OF_WEEK== Calendar.SATURDAY) {
    		return ai;
    	} else if (daycode.contains("7") && Calendar.DAY_OF_WEEK== Calendar.SUNDAY) {
    		return ai;
    	}    		
    	

    	Date date = cal.getTime();
    	
/*    	if (date.getDay() == Calendar.SATURDAY) return 0;
        Log.d ("calarm:", String.format("%tA", cal)); */    	
    	
    	AFW_Time at = new AFW_Time();

/*    	Log.d("calarm: getdate alarmtime: ", String.valueOf(at.getDate(alarmtime, "yyyy.MM.dd HH:mm:ss.SSS")));
    	Log.d("calarm: getdate when: ", String.valueOf(at.getDate(when, "yyyy.MM.dd HH:mm:ss.SSS")));
    	
		Log.d("calarm: time in db was: ", String.valueOf(ai.getAlarmtime()));
		Log.d("calarm: atime-when: ", String.valueOf(alarmtime - when));
*/
		return ai;

		
	}
	/**
	 * 	We now have a list of guaranteed sort order
	 * 	Skip trough list until an alarm object has alarm time set *after* current time
	 * 	This alarm is obviously the first alarm in the queue to set off
	 * 	Unless its a snooze or a repeating alarm
	 * 	But here we figure out if the alarm is supposed to go off today maybe,,,
	 * @param results 
	 * @return AlarmInfo
	 */ 
	private AlarmInfo get_first_alarm_after_current_time(ArrayList<AlarmInfo> results)
	{
    	AlarmInfo ai  = new AlarmInfo();    	
    	Iterator<AlarmInfo> it = results.iterator();
    	boolean no_real_cmp = false;
    	
    	while(it.hasNext())
    	{
    		ai = it.next();
    		long t1 = ai.getAlarmtime();
    		long t2 = System.currentTimeMillis();
    		if (t1 >= t2) {											//	We found an alarm in the future
    			break;   		
    		} else {
    			no_real_cmp = true;
    			ai = null;											// Now we have no alarm that is actually set in the future anymore.
    		}
    	}

    	return ai;
	}

	/**
	 * Sort the alarms by time only, because dates are at this point not in use.
	 * @param results
	 * @return
	 */
	private ArrayList<AlarmInfo> sort_resultset(ArrayList<AlarmInfo> results)
	{	
    	Collections.sort(results, 
    			new Comparator<AlarmInfo>()			
    			{		
					public int compare(AlarmInfo s1, AlarmInfo s2)
					{
			  			  Date d1 	= new Date(s1.getAlarmtime()); 
			  			  Date d2 	= new Date(s2.getAlarmtime());
			  			  int h1 	= d1.getHours();
			  			  int m1 	= d1.getMinutes();
			  			  int h2 	= d2.getHours();
			  			  int m2 	= d2.getMinutes();
			  			  int t1 	= m1 + 60 * h1;
			  			  int t2 	= m2 + 60 * h2;    			  
			  			  return t1 - t2;
		  		  }
    			});
    	return results;
	}
	private ArrayList<AlarmInfo> sort_resultset_full_time(ArrayList<AlarmInfo> results)
	{	
    	Collections.sort(results, 
    			new Comparator<AlarmInfo>()			
    			{		
					public int compare(AlarmInfo s1, AlarmInfo s2)
					{
			  			  long t1	= s1.getAlarmtime(); 
			  			  long t2	= s2.getAlarmtime(); 
			  			  return (int) (t1 - t2);
		  		  }
    			});
    	return results;
	}

}
